1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.base.Function;
21  import com.google.common.base.Functions;
22  import com.google.common.collect.testing.MapInterfaceTest;
23  
24  import java.util.Collection;
25  import java.util.Iterator;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import javax.annotation.Nullable;
30  
31  /**
32   * Tests for {@link Maps#transformValues}.
33   *
34   * @author Isaac Shum
35   */
36  @GwtCompatible
37  public class MapsTransformValuesTest extends MapInterfaceTest<String, String> {
38  
39    /**
40     * Constructor that assigns {@code supportsIteratorRemove} the same value as
41     * {@code supportsRemove}.
42     */
43    protected MapsTransformValuesTest(
44        boolean allowsNullKeys,
45        boolean allowsNullValues,
46        boolean supportsPut,
47        boolean supportsRemove,
48        boolean supportsClear) {
49      super(allowsNullKeys, allowsNullValues, supportsPut, supportsRemove,
50          supportsClear, supportsRemove);
51    }
52  
53    public MapsTransformValuesTest() {
54      super(false, true, false, true, true);
55    }
56  
57    protected Map<String, String> makeEmptyMap() {
58      return Maps.transformValues(Maps.<String, String>newHashMap(), 
59          Functions.<String>identity());
60    }
61  
62    @Override
63    protected Map<String, String> makePopulatedMap() {
64      Map<String, Integer> underlying = Maps.newHashMap();
65      underlying.put("a", 1);
66      underlying.put("b", 2);
67      underlying.put("c", 3);
68      return Maps.transformValues(underlying, Functions.toStringFunction());
69    }
70  
71    @Override protected String getKeyNotInPopulatedMap()
72        throws UnsupportedOperationException {
73      return "z";
74    }
75  
76    @Override protected String getValueNotInPopulatedMap()
77        throws UnsupportedOperationException {
78      return "26";
79    }
80  
81    /** Helper assertion comparing two maps */
82    private void assertMapsEqual(Map<?, ?> expected, Map<?, ?> map) {
83      assertEquals(expected, map);
84      assertEquals(expected.hashCode(), map.hashCode());
85      assertEquals(expected.entrySet(), map.entrySet());
86  
87      // Assert that expectedValues > mapValues and that
88      // mapValues > expectedValues; i.e. that expectedValues == mapValues.
89      Collection<?> expectedValues = expected.values();
90      Collection<?> mapValues = map.values();
91      assertEquals(expectedValues.size(), mapValues.size());
92      assertTrue(expectedValues.containsAll(mapValues));
93      assertTrue(mapValues.containsAll(expectedValues));
94    }
95  
96    public void testTransformEmptyMapEquality() {
97      Map<String, String> map = Maps.transformValues(
98          ImmutableMap.<String, Integer>of(), Functions.toStringFunction());
99      assertMapsEqual(Maps.newHashMap(), map);
100   }
101 
102   public void testTransformSingletonMapEquality() {
103     Map<String, String> map = Maps.transformValues(
104         ImmutableMap.of("a", 1), Functions.toStringFunction());
105     Map<String, String> expected = ImmutableMap.of("a", "1");
106     assertMapsEqual(expected, map);
107     assertEquals(expected.get("a"), map.get("a"));
108   }
109 
110   public void testTransformIdentityFunctionEquality() {
111     Map<String, Integer> underlying = ImmutableMap.of("a", 1);
112     Map<String, Integer> map = Maps.transformValues(
113         underlying, Functions.<Integer>identity());
114     assertMapsEqual(underlying, map);
115   }
116 
117   public void testTransformPutEntryIsUnsupported() {
118     Map<String, String> map = Maps.transformValues(
119         ImmutableMap.of("a", 1), Functions.toStringFunction());
120     try {
121       map.put("b", "2");
122       fail();
123     } catch (UnsupportedOperationException expected) {
124     }
125 
126     try {
127       map.putAll(ImmutableMap.of("b", "2"));
128       fail();
129     } catch (UnsupportedOperationException expected) {
130     }
131 
132     try {
133       map.entrySet().iterator().next().setValue("one");
134       fail();
135     } catch (UnsupportedOperationException expected) {
136     }
137   }
138 
139   public void testTransformRemoveEntry() {
140     Map<String, Integer> underlying = Maps.newHashMap();
141     underlying.put("a", 1);
142     Map<String, String> map
143         = Maps.transformValues(underlying, Functions.toStringFunction());
144     assertEquals("1", map.remove("a"));
145     assertNull(map.remove("b"));
146   }
147 
148   public void testTransformEqualityOfMapsWithNullValues() {
149     Map<String, String> underlying = Maps.newHashMap();
150     underlying.put("a", null);
151     underlying.put("b", "");
152 
153     Map<String, Boolean> map = Maps.transformValues(underlying,
154         new Function<String, Boolean>() {
155           @Override
156           public Boolean apply(@Nullable String from) {
157             return from == null;
158           }
159         }
160     );
161     Map<String, Boolean> expected = ImmutableMap.of("a", true, "b", false);
162     assertMapsEqual(expected, map);
163     assertEquals(expected.get("a"), map.get("a"));
164     assertEquals(expected.containsKey("a"), map.containsKey("a"));
165     assertEquals(expected.get("b"), map.get("b"));
166     assertEquals(expected.containsKey("b"), map.containsKey("b"));
167     assertEquals(expected.get("c"), map.get("c"));
168     assertEquals(expected.containsKey("c"), map.containsKey("c"));
169   }
170 
171   public void testTransformReflectsUnderlyingMap() {
172     Map<String, Integer> underlying = Maps.newHashMap();
173     underlying.put("a", 1);
174     underlying.put("b", 2);
175     underlying.put("c", 3);
176     Map<String, String> map
177         = Maps.transformValues(underlying, Functions.toStringFunction());
178     assertEquals(underlying.size(), map.size());
179 
180     underlying.put("d", 4);
181     assertEquals(underlying.size(), map.size());
182     assertEquals("4", map.get("d"));
183 
184     underlying.remove("c");
185     assertEquals(underlying.size(), map.size());
186     assertFalse(map.containsKey("c"));
187 
188     underlying.clear();
189     assertEquals(underlying.size(), map.size());
190   }
191 
192   public void testTransformChangesAreReflectedInUnderlyingMap() {
193     Map<String, Integer> underlying = Maps.newLinkedHashMap();
194     underlying.put("a", 1);
195     underlying.put("b", 2);
196     underlying.put("c", 3);
197     underlying.put("d", 4);
198     underlying.put("e", 5);
199     underlying.put("f", 6);
200     underlying.put("g", 7);
201     Map<String, String> map
202         = Maps.transformValues(underlying, Functions.toStringFunction());
203 
204     map.remove("a");
205     assertFalse(underlying.containsKey("a"));
206 
207     Set<String> keys = map.keySet();
208     keys.remove("b");
209     assertFalse(underlying.containsKey("b"));
210 
211     Iterator<String> keyIterator = keys.iterator();
212     keyIterator.next();
213     keyIterator.remove();
214     assertFalse(underlying.containsKey("c"));
215 
216     Collection<String> values = map.values();
217     values.remove("4");
218     assertFalse(underlying.containsKey("d"));
219 
220     Iterator<String> valueIterator = values.iterator();
221     valueIterator.next();
222     valueIterator.remove();
223     assertFalse(underlying.containsKey("e"));
224 
225     Set<Map.Entry<String, String>> entries = map.entrySet();
226     Map.Entry<String, String> firstEntry = entries.iterator().next();
227     entries.remove(firstEntry);
228     assertFalse(underlying.containsKey("f"));
229 
230     Iterator<Map.Entry<String, String>> entryIterator = entries.iterator();
231     entryIterator.next();
232     entryIterator.remove();
233     assertFalse(underlying.containsKey("g"));
234 
235     assertTrue(underlying.isEmpty());
236     assertTrue(map.isEmpty());
237     assertTrue(keys.isEmpty());
238     assertTrue(values.isEmpty());
239     assertTrue(entries.isEmpty());
240   }
241 
242   public void testTransformEquals() {
243     Map<String, Integer> underlying = ImmutableMap.of("a", 0, "b", 1, "c", 2);
244     Map<String, Integer> expected
245         = Maps.transformValues(underlying, Functions.<Integer>identity());
246 
247     assertMapsEqual(expected, expected);
248 
249     Map<String, Integer> equalToUnderlying = Maps.newTreeMap();
250     equalToUnderlying.putAll(underlying);
251     Map<String, Integer> map = Maps.transformValues(
252         equalToUnderlying, Functions.<Integer>identity());
253     assertMapsEqual(expected, map);
254 
255     map = Maps.transformValues(ImmutableMap.of("a", 1, "b", 2, "c", 3),
256         new Function<Integer, Integer>() {
257           @Override
258           public Integer apply(Integer from) {
259             return from - 1;
260           }
261         }
262     );
263     assertMapsEqual(expected, map);
264   }
265 
266   public void testTransformEntrySetContains() {
267     Map<String, Boolean> underlying = Maps.newHashMap();
268     underlying.put("a", null);
269     underlying.put("b", true);
270     underlying.put(null, true);
271 
272     Map<String, Boolean> map = Maps.transformValues(
273         underlying, new Function<Boolean, Boolean>() {
274           @Override
275           public Boolean apply(@Nullable Boolean from) {
276             return (from == null) ? true : null;
277           }
278         }
279     );
280 
281     Set<Map.Entry<String, Boolean>> entries = map.entrySet();
282     assertTrue(entries.contains(Maps.immutableEntry("a", true)));
283     assertTrue(entries.contains(Maps.immutableEntry("b", (Boolean) null)));
284     assertTrue(entries.contains(
285         Maps.immutableEntry((String) null, (Boolean) null)));
286 
287     assertFalse(entries.contains(Maps.immutableEntry("c", (Boolean) null)));
288     assertFalse(entries.contains(Maps.immutableEntry((String) null, true)));
289   }
290 
291   @Override public void testKeySetRemoveAllNullFromEmpty() {
292     try {
293       super.testKeySetRemoveAllNullFromEmpty();
294     } catch (RuntimeException tolerated) {
295       // GWT's HashMap.keySet().removeAll(null) doesn't throws NPE.
296     }
297   }
298 
299   @Override public void testEntrySetRemoveAllNullFromEmpty() {
300     try {
301       super.testEntrySetRemoveAllNullFromEmpty();
302     } catch (RuntimeException tolerated) {
303       // GWT's HashMap.entrySet().removeAll(null) doesn't throws NPE.
304     }
305   }
306 }